package xyz.renyumeng.xkm.pay.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import xyz.renyumeng.xkm.dto.shop.ProductInfoDto;
import xyz.renyumeng.xkm.pay.service.PayService;
import xyz.renyumeng.xkm.client.order.OrderFeignClient;
import xyz.renyumeng.xkm.client.shop.ProductInfoFeignClient;
import xyz.renyumeng.xkm.client.user.UserInfoFeignClient;
import xyz.renyumeng.xkm.exception.XkmException;
import xyz.renyumeng.xkm.model.order.OrderInfo;
import xyz.renyumeng.xkm.model.shop.ProductInfo;
import xyz.renyumeng.xkm.model.user.UserInfo;
import xyz.renyumeng.xkm.redis.RedisData;
import xyz.renyumeng.xkm.result.Result;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.concurrent.*;

import static xyz.renyumeng.xkm.redis.RedisConstants.*;

/**
 * @author renyumeng
 * @date Created in 9:58 2023/6/30
 */

@Service
@Slf4j
@Transactional
public class PayServiceImpl implements PayService {
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private UserInfoFeignClient userInfoFeignClient;
    @Resource
    private ProductInfoFeignClient productInfoFeignClient;
    @Resource
    private OrderFeignClient orderFeignClient;

    @Override
    public Result<Long> pay(OrderInfo orderInfo) {
        // TODO 支付业务 支付宝模块
        Boolean payStatus = this.payForAliPay(orderInfo);
        if (!payStatus) {
            throw new XkmException(201, "支付失败!");
        }

        Long orderId = orderInfo.getId();
        Long userId = orderInfo.getPurchaseUserId();
        String messageKey = QUEUE_ORDER_KEY + userId + ":" + orderId;
        // 判断redis中是否存在缓存，不存在则为两种情况，用户主动取消，订单过期
        String orderInfoCache = stringRedisTemplate.opsForValue().get(messageKey);
        if (StrUtil.isBlank(orderInfoCache)) {
            throw new XkmException(201, "该订单不存在!");
        }

        OrderInfo orderInfoPersistence = JSONUtil.toBean(orderInfoCache, OrderInfo.class);
        orderInfoPersistence.setId(orderId);
        orderInfoPersistence.setIsPayed(0);
        String now = DateUtil.now();
        // 写入数据库 远程调用order接口写入数据库
        // TODO 解耦发送到消息队列
        orderFeignClient.innerAddOrder(orderInfoPersistence);
        // 调用shopService商品已经卖出
        ProductInfoDto productInfoDto = new ProductInfoDto();
        productInfoDto.setId(orderInfoPersistence.getProductId());
        productInfoDto.setIsSoled(1);
        productInfoFeignClient.updateProduct(productInfoDto);

        // 开启一个新线程写缓存
        DATA_PERSISTENCE_EXECUTOR.submit(() -> {
            // 写入redis
            // 远程调用userService和shopService将id转换成对应的名称
            OrderInfo redisData = this.transferId2RedisData(orderInfoPersistence);
            String key = ORDER_INFO_KEY + userId.toString();
            stringRedisTemplate.opsForHash().put(key, orderId.toString(), JSONUtil.toJsonStr(redisData));

            // 写入逻辑过期时间
            RedisData redisData2Expire = new RedisData();
            redisData2Expire.setData(null);
            redisData2Expire.setExpireTime(LocalDateTime.now().plusDays(TimeUnit.DAYS.toDays(ORDER_INFO_TTL)));
            stringRedisTemplate.opsForHash().put(key, "expireTime", JSONUtil.toJsonStr(redisData2Expire));
        });
        // 返回支付成功信息
        return Result.ok(orderId).message("支付成功!");
    }

    /**
     * 将订单id，用户信息，商品信息（详情和名称），卖家信息（名称）
     *
     * @param orderInfo 用户信息
     * @return {@link RedisData}
     */
    private OrderInfo transferId2RedisData(OrderInfo orderInfo) {
        Long purchaseUserId = orderInfo.getPurchaseUserId();
        Long ownerId = orderInfo.getOwnerId();
        Long productId = orderInfo.getProductId();

        Date createTime = orderInfo.getCreateTime();
        log.info("createTIme:" + createTime);


        // 获取购买者和提供者的信息
        UserInfo purchaseUserInfo = userInfoFeignClient.innerGetUserByIdWithAddressInfo(purchaseUserId);
        if (ObjectUtil.isEmpty(purchaseUserInfo)) {
            throw new XkmException(201, "购买用户信息不存在!");
        }
        UserInfo ownerUserInfo = userInfoFeignClient.innerGetUserByIdWithAddressInfo(ownerId);
        if (ObjectUtil.isEmpty(ownerUserInfo)) {
            throw new XkmException(201, "发布用户信息不存在!");
        }

        ProductInfo productInfo = productInfoFeignClient.innerGetProductInfoAndOwnerUserInfoById(productId);
        if (ObjectUtil.isEmpty(productInfo)) {
            throw new XkmException(201, "商品信息不存在!");
        }
        Boolean productSoledResult = productInfoFeignClient.innerUpdateSoledInfoById(productId);

        if (BooleanUtil.isFalse(productSoledResult)) {
            throw new XkmException(201, "商品出售信息修改失败!");
        }
        orderInfo.getParam().put("purchaseUserName", purchaseUserInfo.getUsername());
        orderInfo.getParam().put("purchaseUserAddressInfo", purchaseUserInfo.getParam().get("addressInfo"));
        orderInfo.getParam().put("ownerUserName", ownerUserInfo.getUsername());
        orderInfo.getParam().put("productName", productInfo.getProductName());
        orderInfo.getParam().put("productDesc", productInfo.getDesc());
        orderInfo.getParam().put("productType", productInfo.getParam().get("type"));
        orderInfo.getParam().put("createTime", orderInfo.getCreateTime());
        Object addressObject = purchaseUserInfo.getParam().get("addressInfo");
        String address = StrUtil.toString(addressObject);
        orderInfo.setAddress(address);

        return orderInfo;
    }

    private static final ExecutorService DATA_PERSISTENCE_EXECUTOR = new ThreadPoolExecutor(
            10,
            15,
            3,
            TimeUnit.SECONDS,
            new LinkedBlockingDeque<>(3),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.DiscardOldestPolicy());

    private Boolean payForAliPay(OrderInfo orderInfo) {
        // TODO 支付业务 支付宝模块
        return true;
    }
}
